package com.bsj.power.common.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bsj.power.common.def.entity.HighRise;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author yinhao
 * @version 1.0
 * @description 多媒体设备查找
 * @date 2024/5/24
 */
@Slf4j
public class FindMediaDevUtils {

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static
    class Camera {
        String sn;
        double x;
        double y;
        double z;
    }

    /**
     * 查看设备是否在此区域中
     *
     * @param cameras
     * @param minX
     * @param maxX
     * @param minY
     * @param maxY
     * @param minZ
     * @param maxZ
     * @return
     */
    public static List<String> findCamerasInRegion(List<Camera> cameras, double minX, double maxX,
                                                   double minY, double maxY, double minZ, double maxZ) {
        List<String> snList = new ArrayList<>();
        for (Camera camera : cameras) {
            if (isInRegion(camera, minX, maxX, minY, maxY, minZ, maxZ)) {
                snList.add(camera.getSn());
            }
        }
        return snList;
    }

    /**
     * 判断设备的位置是否在对应的区域位置点中
     *
     * @param camera
     * @param minX
     * @param maxX
     * @param minY
     * @param maxY
     * @param minZ
     * @param maxZ
     * @return
     */
    private static boolean isInRegion(Camera camera, double minX, double maxX,
                                      double minY, double maxY, double minZ, double maxZ) {
        double x = camera.getX();
        double y = camera.getY();
        double z = camera.getZ();
        return x >= minX && x <= maxX && y >= minY && y <= maxY && z >= minZ && z <= maxZ;
    }

    /**
     * 根据三维空间判断位置是否在围栏范围内
     * @param x
     * @param y
     * @param z 高度
     * @param areaCoordinates 围栏位置点
     * @param minHeight 最小高度
     * @param maxHeight 最大高度
     * @return
     */
    public static boolean IsThreeDInPoly(Double x, Double y, Double z,
                                         String areaCoordinates, Double minHeight, Double maxHeight) {
        boolean isPtInPoly = IsPtInPoly(x, y, areaCoordinates);
        if (isPtInPoly) {
            //在平面范围内的话,还需判断高度范围
            if (minHeight <= z && maxHeight > z) {
                return true;
            } else {
                return false;
            }
        }
        return false;
    }

    /**
     * 根据mapId判断高度
     * @param x
     * @param y
     * @param areaCoordinates
     * @param posMapId
     * @param pathMapId
     * @return
     */
    public static boolean IsPathMapInPoly(Double x, Double y,
                                         String areaCoordinates, Integer posMapId, Integer pathMapId) {
//        if (posMapId == 34) {
//            //1号机组室外
//            x += 208.619;
//        }
        boolean isPtInPoly = IsPtInPoly(x, y, areaCoordinates);
        if (isPtInPoly) {
            //在平面范围内的话,还需判断高度范围
            if (posMapId.equals(pathMapId)) {
                return true;
            } else {
                return false;
            }
        }
        return false;
    }

    /**
     * 判断一个点是否在一个多边形中
     *
     * @param x 点位 x  例：50
     * @param y 点位 y  例：50
     * @param areaCoordinates 多边形坐标  例：“0,0|100,0|100,100|0,100”
     * @return
     */
    public static boolean IsPtInPoly(Double x, Double y, String areaCoordinates) {
        //测试点位坐标
        Point2D.Double point = new Point2D.Double(x, y);
        String[] areaCoordinateslist = areaCoordinates.split("\\|");
        List<Point2D.Double> pts = new ArrayList<>();


//        if (areaCoordinateslist.length % 2 == 0) {
//            for (int i = 0; i < areaCoordinateslist.length / 2; i++) {
//                int min = i * 2;
//                int max = i * 2 + 1;
//                pts.add(new Point2D.Double(Double.parseDouble(areaCoordinateslist[min]), Double.parseDouble(areaCoordinateslist[max])));
//            }
//        }
        for (String areaSplit : areaCoordinateslist) {
            String[] split = areaSplit.split(",");
            if (split.length % 2 == 0) {
                pts.add(new Point2D.Double(Double.parseDouble(split[0]), Double.parseDouble(split[1])));
            }
        }

        int N = pts.size();
        //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
        boolean boundOrVertex = true;
        //交叉点计数x
        int intersectCount = 0;
        //浮点类型计算时候与0比较时候的容差
        double precision = 2e-10;
        //相邻边界顶点
        Point2D.Double p1, p2;
        //当前点
        Point2D.Double p = point;
        //左顶点
        p1 = pts.get(0);
        //检查所有射线
        for (int i = 1; i <= N; ++i) {
            if (p.equals(p1)) {
                //p是一个顶点
                return boundOrVertex;
            }

            //右顶点
            p2 = pts.get(i % N);
            //不符合
            if (p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)) {
                p1 = p2;
                continue;//next ray left point
            }

            //线被算法越过（的公共部分）
            if (p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)) {
                //x在射线之前
                if (p.y <= Math.max(p1.y, p2.y)) {
                    //覆盖在水平射线上
                    if (p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)) {
                        return boundOrVertex;
                    }
                    //射线是垂直的
                    if (p1.y == p2.y) {
                        //覆盖在垂直射线上
                        if (p1.y == p.y) {
                            return boundOrVertex;
                        } else {
                            //在射线前
                            ++intersectCount;
                        }
                    } else {
                        //左侧的交叉点
                        //y的交叉点
                        double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        if (Math.abs(p.y - xinters) < precision) {
                            //覆盖在射线上
                            return boundOrVertex;
                        }
                        if (p.y < xinters) {
                            //射线前
                            ++intersectCount;
                        }
                    }
                }
            } else {
                //光线穿过顶点时的特殊情况
                if (p.x == p2.x && p.y <= p2.y) {
                    //p跨越p2
                    //下一个顶点
                    Point2D.Double p3 = pts.get((i + 1) % N);
                    //p.x位于p1.x和p3.x之间
                    if (p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)) {
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            //下一条射线左点
            p1 = p2;
        }
        if (intersectCount % 2 == 0) {
            //偶数在多边形外
            return false;
        } else {
            //奇数在多边形内
            return true;
        }
    }



    /**
     * 判断某个位置点是否在房间区域内
     * @param highRises 高层范围集合
     * @param roomPoint 房间的开始坐标点和结束坐标点(正方形)
     * @param pos 待判断的坐标点
     * @return
     */
    public static boolean judgeScenePos(List<HighRise> highRises, String roomPoint, double[] pos) {
        if (roomPoint == null) {
            return false;
        }
        JSONObject jsonObject = JSON.parseObject(roomPoint);
        JSONArray startArray = jsonObject.getJSONArray("start");
        JSONArray endArray = jsonObject.getJSONArray("end");
        //矩形的高度是一致的
        Double rectY = startArray.getDouble(1);
        //判断出矩形的范围
        AtomicReference<Double> rectMinY = new AtomicReference<>(0d);
        AtomicReference<Double> rectMaxY = new AtomicReference<>(0d);
        highRises.stream().forEach(item -> {
            Double riseMin = item.getRiseMin();
            Double riseMax = item.getRiseMax();
            if (rectY >= riseMin && rectY < riseMax) {
                rectMinY.set(riseMin);
                rectMaxY.set(riseMax);
            }
        });
        //已知起点跟对角线的点,计算出第二跟第四的点
        //[1,0.1,0]
        Double[] onePos = new Double[]{startArray.getDouble(0), rectY, startArray.getDouble(2)};
        //[2,0.1,0]
        Double[] twoPos = new Double[]{endArray.getDouble(0), rectY, startArray.getDouble(2)};
        //[2,0.1,1]
        Double[] therePos = new Double[]{endArray.getDouble(0), rectY, endArray.getDouble(2)};
        //[1,0.1,1]
        Double[] fourPos = new Double[]{startArray.getDouble(0), rectY, endArray.getDouble(2)};
        List<Double[]> pointList = new ArrayList<>();
        pointList.add(onePos);
        pointList.add(twoPos);
        pointList.add(therePos);
        pointList.add(fourPos);
        double maxX = onePos[0];
        double minX = onePos[0];
        double maxZ = onePos[2];
        double minZ = onePos[2];
        for (int i = 0; i < pointList.size(); i++) {
            Double[] doubles = pointList.get(i);
            double x1 = doubles[0];
            double z1 = doubles[2];
            //最大值
            maxX = x1 > maxX ? x1 : maxX;
            maxZ = z1 > maxZ ? z1 : maxZ;
            //最小值
            minX = x1 < minX ? x1 : minX;
            minZ = z1 < minZ ? z1 : minZ;
        }
        boolean inRegion = isInRegion(new Camera("", pos[0], pos[1], pos[2]), minX, maxX,
                rectMinY.get(), rectMaxY.get(), minZ, maxZ);
        return inRegion;
    }

    public static void main(String[] args) {
//        long millis = System.currentTimeMillis();
        System.out.println(IsPtInPoly(50d,50d,"0,0|100,0|100,100|0,100"));
//        System.out.println(System.currentTimeMillis() - millis);
        List<HighRise> highRises = new ArrayList<>();
        highRises.add(HighRise.builder().highId(1).riseMin(-12.00).riseMax(0.10).build());
        highRises.add(HighRise.builder().highId(2).riseMin(0.10).riseMax(2.50).build());
        highRises.add(HighRise.builder().highId(3).riseMin(2.50).riseMax(11.00).build());
        highRises.add(HighRise.builder().highId(4).riseMin(11.00).riseMax(20.00).build());
        highRises.add(HighRise.builder().highId(5).riseMin(20.00).riseMax(31.00).build());
        String scenePoint = "{\"start\":[52.9344456508184,2.5,4.407502347376623],\"end\":[63.894487968059224,2.5,-6.625898596256983],\"floor\":2}";
        double[] pos = new double[]{52.9344456508184,2.49,4.407502347376623};
        System.out.println(judgeScenePos(highRises, scenePoint, pos));
    }

}
